<?php
/**
 * Library Of Shared Code (LOSC)
 *
 * LICENSE
 *
 * This source file is subject to the New BSD License that is bundled
 * with this package in the file LICENSE.txt.
 *
 * @category   LOSC Modules
 * @package    User
 * @subpackage Controllers
 * @copyright  Copyright (c) 2008 Robin Skoglund (http://robinsk.net/)
 * @license    http://creativecommons.org/licenses/BSD/  New BSD License
 */

/**
 * Controller for handling user registration
 * 
 * @category   LOSC Modules
 * @package    User
 * @subpackage Controllers
 * @copyright  Copyright (c) 2008 Robin Skoglund (http://robinsk.net/)
 * @license    http://creativecommons.org/licenses/BSD/  New BSD License
 */
class User_RegisterController extends Zym_Controller_Action_Abstract 
{
    /**
     * URL helper
     * 
     * @var Zend_Controller_Action_Helper_Url
     */
    protected $_urlHelper;
    
    /**
     * Session namespace
     * 
     * @var Zend_Session_Namespace
     */
    protected $_session;
    
    /**
     * Initializes controller
     * 
     * @return void
     */
    public function init()
    {
        parent::init();
        
        $this->_urlHelper = $this->getHelper('Url');
        $this->_session = new Zend_Session_Namespace(__CLASS__);
    }
    
    /**
     * Shows registration form if not already logged in
     *
     * @return void
     */
    public function indexAction()
    {
        $this->view->form = $this->_getRegistrationForm();
        
        if ($this->getRequest()->isPost()) {
            $this->view->form->isValid($_POST);
        }
    }
    
    /**
     * Processes a registration form
     * 
     * @return void
     */
    public function processAction()
    {
        if (!$this->getRequest()->isPost()) {
            $this->_internalRedirect();
        }
        
        $form = $this->_getRegistrationForm();
        
        if ($form->isValid($_POST)) {
            // form is valid, create new user
            $user               = new App_User();
            $user->username     = $form->getValue('username');
            $user->email        = $form->getValue('email');
            $user->password     = sha1($form->getValue('password1'));
            $user->realname     = $form->getValue('realname');
            $user->confirmation = md5(uniqid('kaypaste', true));
            
            if ($user->trySave()) {
                // get confirmation mail for the user
                $mail = $this->_getConfirmationMail($user);
                
                try {
                    // try to send the mail
                    $mail->send();
                    $this->_session->confirmationSent = $user->email;
                    
                    // success!
                    $this->_internalRedirect('confirmation-sent', true);
                } catch(Exception $e) {
                    // unable to send mail
                    $this->_session->confirmationError = 'Unable to send mail';
                    $this->_internalRedirect('error', true);
                }
            } else {
                // unable to save user to database
                $this->_session->confirmationError = 'Unable to save to database';
                $this->_internalRedirect('error', true);
            }
        } else {
            // form is not valid
            $this->view->form = $form;
        }
    }
    
    /**
     * Tells the user that a confirmation email has been sent
     * 
     * @return void
     */
    public function confirmationSentAction()
    {
        // redirect if confirmation has not been sent
        $sent = isset($this->_session->confirmationSent)
              ? $this->_session->confirmationSent
              : false;

        if (!$sent) {
            $this->_internalRedirect();
        }
        
        $this->view->confirmationSent = $this->_session->confirmationSent;
    }
    
    /**
     * Confirms a user registration
     * 
     * @return void
     */
    public function confirmAction()
    {
        // get flag from session indicating whether confirmation is sent 
        $valid = isset($this->_session->confirmationSent)
              ? $this->_session->confirmationSent
              : false;
        
        // get confirmation params
        $confirmationId = $this->getRequest()->getUserParam('id', '0');
        $confirmationEmail = $this->getRequest()->getUserParam('email', '0');
        
        // validate params
        if (!@preg_match('/^[a-f0-9]{32}$/im', $confirmationId)) {
            $sent = false;
        } else {
            try {
                $validator = new Zend_Validate_EmailAddress();
                if (!$validator->isValid($confirmationEmail)) {
                    $sent = false;
                }
            } catch(Exception $e) {
                $sent = false;
            }
        }

        // redirect if confirmation has not been sent, or invalid/missing params
        if (!$sent) {
            $this->_internalRedirect();
        }
        
        // okay, time to do the confirmation
        $q = Doctrine_Query::create()
                ->update('Kaypaste_User u')
                ->set('u.confirmation', 'null')
                ->where('u.confirmation = ? AND u.email = ?',
                        array($confirmationId, $confirmationEmail));
        
        try {
            $result = $q->execute();
            
            if (count($result) > 0) {
                // success
            } else {
                $this->view->confirmationMessage = 'No matching record found';
                $this->render('confirm-failure');
            }
            
            // prevent this page from being loaded again
            unset($this->_session->confirmationSent);
        } catch(Exception $e) {
            $this->view->confirmationMessage = 'Error updating table.';
            $this->render('confirm-failure');
        }
    }
    
    /**
     * Says that we're sorry that the registration could not complete
     * 
     * @return void
     */
    public function errorAction()
    {
        // redirect if an error didn't occur 
        if (!isset($this->_session->confirmationError)) {
            $this->_internalRedirect();
        }
        
        // prevent this page from being loaded again
        unset($this->_session->confirmationError);
    }
    
    /**
     * Performs an internal redirect
     * 
     * @param  string  $action   [optional] action to redirect to,
     *                           defaults to 'index'
     * @param  boolean $getOnly  [optinoal] if set true, the redirect will
     *                           be using code 303 (GET only)
     * @return void
     */
    protected function _internalRedirect($action = 'index',
                                         $getOnly = false)
    {
        $uri = $this->_urlHelper->url(array(
            'module'     => 'user',
            'controller' => 'register',
            'action'     => $action
        ), 'default', true);
        
        $this->_redirect($uri, array('code' => $getOnly ? 303 : 302));
    }
    
    /**
     * Returns a user registration form
     * 
     * @return User_Form_Register  user registration form
     */
    protected function _getRegistrationForm()
    {
        $actionUri = $this->_urlHelper->url(array(
            'module'     => 'user',
            'controller' => 'register',
            'action'     => 'process'
        ), 'default', true);
        
        $form = $this->getHelper('Form')->create('Register');
        $form->setAction($actionUri);
        
        return $form;
    }
    
    /**
     * Returns a ready-to-send confirmation mail for the given user
     * 
     * @param Kaypaste_User $user  user
     */
    protected function _getConfirmationMail($user)
    {
        $confirmationUri = $this->view->absoluteUrl(array(
            'id' => $user->confirmation,
            'email' => $user->email
        ), 'user_reg_confirm', true);
        
        $mail = new Zend_Mail();
        $mail->setFrom('no-reply@robinsk.net', 'robinsk.net');
        $mail->setSubject('Registration Confirmation');
        $mail->addTo($user->email);
        
        $body = 'Hey ' . $user->realname . ",\n\n"
              . 'You recently registered for robinsk.net. '
              . "To complete your registration, follow this link:\n"
              . "$confirmationUri\n\n"
              . "If you did not register, just ignore this message\n\n"
              . "Thanks,\n"
              . "robinsk";
        
        $mail->setBodyText($body);
        
        return $mail;
    }
}
